#include "../../common.hpp"
#include <cerrno>
#include <errno.h>
#include <fcntl.h>
#include <iostream>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>

const size_t PORT = 8085;
const size_t MAX_EVENTS = 10;

class epoll_server {
public:
    static epoll_server &GetInstance() {
        static epoll_server es;
        return es;
    }

    void run() {
        if (!init()) {
            std::cerr << "init error, quit" << std::endl;
            return;
        }

        while (true) {
            struct epoll_event event;
            struct epoll_event events[MAX_EVENTS];
            int n_ready = epoll_wait(_epfd, events, MAX_EVENTS, -1);
            if (n_ready == -1) {
                std::cerr << "epoll_wait error, quit" << std::endl;
                std::cerr << strerror(errno) << std::endl;
                return;
            }

            for (int i = 0; i < n_ready; ++i) {
                if (events[i].data.fd == _listen_fd) {
                    while (true) {
                        struct sockaddr_in client_addr;
                        socklen_t client_len = sizeof(client_addr);
                        int conn_fd = accept(_listen_fd, (struct sockaddr *) &client_addr, &client_len);
                        if (conn_fd < 0) {
                            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                                break;
                            }
                            std::cerr << "accrpt error, quit" << std::endl;
                            break;
                        }
                        char client_ip[INET_ADDRSTRLEN];
                        std::cout << "New Connection from : " << inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN) << std::endl;

                        int flags = fcntl(conn_fd, F_GETFL, 0);
                        fcntl(conn_fd, F_SETFL, flags | O_NONBLOCK);

                        event.data.fd = conn_fd;
                        event.events = EPOLLIN;
                        if (epoll_ctl(_epfd, EPOLL_CTL_ADD, conn_fd, &event) == -1) {
                            std::cerr << "epoll_ctl error" << std::endl;
                            close(conn_fd);
                        }
                    }
                } else {
                    int client_fd = events[i].data.fd;
                    char buffer[1024] = {0};
                    int n = read(client_fd, buffer, sizeof(buffer) - 1);
                    if (n < 0) {
                        std::cerr << "read error" << std::endl;
                        close(client_fd);
                    } else if (n == 0) {
                        std::cout << "client fd " << client_fd << " close" << std::endl;
                        close(client_fd);
                    } else {
                        std::cout << "Reveived from fd " << client_fd << " : " << buffer << std::endl;
                        write(client_fd, buffer, n);
                    }
                }
            }
        }
    }

private:
    bool init() {
        _listen_fd = create_and_listen(PORT);
        if (_listen_fd < 0) {
            std::cerr << "create_and_listen error" << std::endl;
            return false;
        }

        _epfd = epoll_create(1);
        struct epoll_event event;
        event.data.fd = _listen_fd;
        event.events = EPOLLIN;
        if (epoll_ctl(_epfd, EPOLL_CTL_ADD, _listen_fd, &event) == -1) {
            std::cerr << "epoll_ctl error" << std::endl;
            return false;
        }

        return true;
    }

    epoll_server() = default;
    epoll_server(const epoll_server &es) = delete;
    epoll_server &operator=(const epoll_server &er) = delete;
    ~epoll_server() {
        close(_listen_fd);
        close(_epfd);
    }

private:
    int _listen_fd;
    int _epfd;
};

int main() {
    epoll_server &es = epoll_server::GetInstance();
    es.run();

    return 0;
}